package handler

import (
	cmn "common"
	cfg "config"
	"db"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"meta"
	"mq"
	"net/http"
	"os"
	"store/ceph"
	"store/oss"
	"strconv"
	"strings"
	"time"
	"util"
)

const (
	FILE_DIR = "/home/rui/go_project/filestore_server/file/"
)

//上传文件
func UploadHandler(w http.ResponseWriter, r *http.Request) {
	if r.Method == "GET" {
		//返回上传页面
		data, err := ioutil.ReadFile("./static/view/index.html")
		if err != nil {
			io.WriteString(w, "internet server error")
			return
		}
		io.WriteString(w, string(data))
	} else if r.Method == "POST" {
		//接受文件流,保存文件
		file, header, err := r.FormFile("file")
		if err != nil {
			fmt.Printf("failed to get data err: %s\n", err.Error())
			w.WriteHeader(http.StatusInternalServerError)
			return
		}
		defer file.Close()

		fileMeta := meta.FileMeta{
			FileName: header.Filename,
			Location: FILE_DIR + header.Filename,
			UploadAt: time.Now().Format("2006-01-02 15:04:05"),
		}
		newFile, err := os.Create(fileMeta.Location)
		if err != nil {
			fmt.Printf("failed to get data err: %s\n", err.Error())
			w.WriteHeader(http.StatusInternalServerError)
			return
		}
		defer newFile.Close()

		fileMeta.FileSize, err = io.Copy(newFile, file)
		if err != nil {
			fmt.Printf("failed to get data err: %s\n", err.Error())
			return
		}

		// 同步或异步将文件转移到Ceph/OSS
		newFile.Seek(0, 0) // 游标重新回到文件头部
		mergePath := cfg.MergeLocalRootDir + fileMeta.FileSha1

		if cfg.CurrentStoreType == cmn.StoreCeph {
			// 文件写入Ceph存储
			data, _ := ioutil.ReadAll(newFile)
			cephPath := "/ceph/" + fileMeta.FileSha1
			err = ceph.PutObject("userfile", cephPath, data)
			if err != nil {
				fmt.Println("upload ceph err: " + err.Error())
				w.Write([]byte("Upload failed!"))
				return
			}
			fileMeta.Location = cephPath
		} else if cfg.CurrentStoreType == cmn.StoreOSS {
			// 文件写入OSS存储
			ossPath := "oss/" + fileMeta.FileSha1
			// 判断写入OSS为同步还是异步
			if !cfg.AsyncTransferEnable {
				// 文件写入OSS存储
				ossPath := "oss/" + fileMeta.FileSha1
				err = oss.Bucket().PutObject(ossPath, newFile)
				if err != nil {
					fmt.Println("upload oss err: " + err.Error())
					w.Write([]byte("Upload failed!"))
					return
				}
				fileMeta.Location = ossPath
			} else {
				// 文件尚未转移，暂存于本地mergePath
				fileMeta.Location = mergePath
				// 写入异步转移任务队列
				data := mq.TransferData{
					FileHash:      fileMeta.FileSha1,
					CurLocation:   fileMeta.Location,
					DestLocation:  ossPath,
					DestStoreType: cmn.StoreOSS,
				}
				pubData, _ := json.Marshal(data)
				pubSuc := mq.Publish(
					cfg.TransExchangeName,
					cfg.TransOSSRoutingKey,
					pubData,
				)
				if !pubSuc {
					// TODO: 当前发送转移信息失败，稍后重试
				}
			}
		}

		//newFile.Seek(0, 0)
		//fileMeta.FileSha1 = util.FileMD5(newFile)
		//保持文件信息
		metaDB := meta.UpdateFileMetaDB(fileMeta)
		if !metaDB {
			w.WriteHeader(http.StatusInternalServerError)
			return
		}
		// 保持用户文件信息
		r.ParseForm()
		userName := r.Form.Get("username")
		finished := db.OnUserFileUploadFinished(userName, fileMeta.FileSha1, fileMeta.FileName, fileMeta.FileSize)
		if !finished {
			w.WriteHeader(http.StatusInternalServerError)
			return
		}
		http.Redirect(w, r, "/static/view/home.html", http.StatusFound)
	}
}

// 文件上传成功
func UploadSucHandler(w http.ResponseWriter, r *http.Request) {
	io.WriteString(w, "Upload Success")
}

// 获取文件原信息
func GetFileMetaHandler(w http.ResponseWriter, r *http.Request) {
	r.ParseForm()
	fileHash := r.Form["filehash"][0]
	fileMeta := meta.GetFileMetaDB(fileHash)
	data, err := json.Marshal(fileMeta)
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		return
	}
	w.Write(data)
}

//获取指定数量文件原信息
func FileQueryHandler(w http.ResponseWriter, r *http.Request) {
	r.ParseForm()
	userName := r.Form.Get("username")
	limitCount, _ := strconv.Atoi(r.Form.Get("limit"))
	fileMetas, err := db.QueryUserFileMetas(userName, limitCount)
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		return
	}
	data, err := json.Marshal(fileMetas)
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		return
	}
	w.Write(data)
}

// 下载文件
func DownloadHandler(w http.ResponseWriter, r *http.Request) {
	r.ParseForm()
	fileHash := r.Form.Get("fileHash")
	fileMeta := db.GetFiledMeta(fileHash)

	if fileMeta.FileAddr.String == "" {
		w.WriteHeader(http.StatusInternalServerError)
		return
	}
	file, err := os.Open(fileMeta.FileAddr.String)
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		return
	}
	defer file.Close()

	data, err := io.ReadAll(file)
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		return
	}
	w.Header().Set("Content-Type", "application/octect-stream")
	w.Header().Set("content-disposition", "attachment; filename=\""+fileMeta.FileName.String+"\"")
	w.Write(data)
}

//重命名
func FileMetaUpdateHandler(w http.ResponseWriter, r *http.Request) {
	r.ParseForm()

	openType := r.Form.Get("op")
	if openType != "0" {
		w.WriteHeader(http.StatusForbidden)
		return
	}
	if r.Method != "POST" {
		w.WriteHeader(http.StatusMethodNotAllowed)
		return
	}
	fileSha1 := r.Form.Get("filehash")
	newFileName := r.Form.Get("filename")
	username := r.Form.Get("username")
	name := db.UpdateUserFileName(fileSha1, newFileName, username)
	if !name {
		w.WriteHeader(http.StatusInternalServerError)
		return
	}
	w.WriteHeader(http.StatusOK)
	w.Write([]byte("ok"))
}

//删除文件
func FileDeleteHandler(w http.ResponseWriter, r *http.Request) {
	if r.Method != "POST" {
		w.WriteHeader(http.StatusMethodNotAllowed)
		return
	}
	r.ParseForm()
	fileSha1 := r.Form.Get("filehash")
	username := r.Form.Get("username")
	del := db.DeleteUserFileName(fileSha1, username)
	if !del {
		w.WriteHeader(http.StatusInternalServerError)
		return
	}
	w.WriteHeader(http.StatusOK)
	w.Write([]byte("ok"))
}

//文件秒传
func TryFastUploadHandler(w http.ResponseWriter, r *http.Request) {
	r.ParseForm()

	//1.解析参数
	username := r.Form.Get("username")
	filehash := r.Form.Get("filehash")
	filename := r.Form.Get("filename")
	filesize, _ := strconv.Atoi(r.Form.Get("filesize"))
	//2.从文件表中获取相同hash文件
	fileMeta := meta.GetFileMetaDB(filehash)

	if fileMeta.FileSha1 == "" {
		resp := util.RespMsg{
			Code: -1,
			Msg:  "秒传失败，请求按照普通文件进行上传",
		}
		w.Write(resp.JSONBytes())
		return
	}
	finished := db.OnUserFileUploadFinished(username, filehash, filename, int64(filesize))
	if finished {
		resp := util.RespMsg{
			Code: -0,
			Msg:  "秒传成功",
		}
		w.Write(resp.JSONBytes())
		return
	} else {
		resp := util.RespMsg{
			Code: -1,
			Msg:  "秒传失败，请稍后重试",
		}
		w.Write(resp.JSONBytes())
	}
}

func DownloadURLHandler(w http.ResponseWriter, r *http.Request) {
	filehash := r.Form.Get("filehash")
	// 从文件表查找记录
	row := db.GetFiledMeta(filehash)

	fmt.Println("fileAddr: " + row.FileAddr.String)
	// 判断文件存在OSS，还是Ceph，还是在本地
	if strings.HasPrefix(row.FileAddr.String, cfg.MergeLocalRootDir) ||
		strings.HasPrefix(row.FileAddr.String, "/ceph") {
		username := r.Form.Get("username")
		token := r.Form.Get("token")
		tmpURL := fmt.Sprintf(
			"http://%s/file/download?filehash=%s&username=%s&token=%s",
			r.Host,
			filehash,
			username,
			token)
		w.Write([]byte(tmpURL))
	} else if strings.HasPrefix(row.FileAddr.String, "oss/") {
		// oss下载url
		signedURL := oss.DownloadURL(row.FileAddr.String)
		w.Write([]byte(signedURL))
	} else {
		w.Write([]byte("Error: 下载链接暂时无法生成"))
	}
}
